package com.yc.mybatis.core;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

/**
 * 数据库连接池
 * @author 3456
 * Company 源辰
 * @date 2023年2月21日
 */
public class ConnectionPool {
	private String driver;
	private String url;
	private String username;
	private String password;
	
	private int initConnections = 5;	// 初始化连接数
	private int maxConnections = 20;	// 最大连接数
	private int incrementConnections = 5;	// 连接池中连接数不够时，每次增加多少
	private Vector<PoolConnection> connections;	//存放创建的连接
	
	public ConnectionPool(String driver, String url, String username, String password) {
		super();
		this.driver = driver;
		this.url = url;
		this.username = username;
		this.password = password;
	}
	
	public int getInitConnections() {
		return initConnections;
	}

	public void setInitConnections(int initConnections) {
		this.initConnections = initConnections;
	}

	public int getMaxConnections() {
		return maxConnections;
	}

	public void setMaxConnections(int maxConnections) {
		this.maxConnections = maxConnections;
	}

	public int getIncrementConnections() {
		return incrementConnections;
	}

	public void setIncrementConnections(int incrementConnections) {
		this.incrementConnections = incrementConnections;
	}
	
	/**
	 * 创建连接池
	 * @throws Exception
	 */
	public synchronized void createPool() throws Exception{
		if (connections != null) {
			return;
		}
		
		// 加载获取驱动
		Driver driver = (Driver) Class.forName(this.driver).newInstance();
		
		// 注册驱动
		DriverManager.registerDriver(driver);
	
		// 初始化连接池列表
		connections = new Vector<PoolConnection>();
	
		// 创建指定数据的初始化连接数
		createConnections(this.initConnections);
		
		System.out.println("连接池创建完成。。");
	}
	
	/**
	 * 创建指定连接数的方法
	 * @param initConnections
	 * @throws SQLException
	 */
	private void createConnections(int initConnections) throws SQLException{
		for(int i = 0; i < initConnections; ++i) {
			if(this.maxConnections > 0 && this.connections.size() >= this.maxConnections) {
				return;
			}
			
			//创建一个连接放到连接池
			connections.addElement(new PoolConnection(newConnection(), this.connections.size() + 1));
		}
		
	}
	
	/**
	 * 创建一个新连接的方法
	 * @return
	 * @throws SQLException
	 */
	private Connection newConnection() throws SQLException{
		Connection con = DriverManager.getConnection(url,username,password);	//获取一个新的连接
		
		// 判断是不是第一次创建，如果第一次创建，则要判断最大连接数是否合理
		if (connections.size() == 0) { // 说明连接池中还没有连接，也就是说现在创建的是第一个连接
			DatabaseMetaData metaData = con.getMetaData();
			int driverMaxConnections = metaData.getMaxConnections(); // 获取数据库所支持的最大连接数
			System.out.println(metaData.getMaxConnections());
			if(driverMaxConnections > 0 && driverMaxConnections < this.maxConnections) {
				this.maxConnections = driverMaxConnections;
			}
			
		}
		return con;
	}
	
	/**
	 * 获取一个可用链接的方法
	 * @return
	 */
	public synchronized Connection getConnection() {
		//说明连接池没有初始化
		if(connections == null || connections.isEmpty()) {
			//System.out.println("连接池没有初始化");
			return null;
		}
		
		Connection con = null;
		try {
			con = getFreeConnection();
			while(con == null) {	//说明连接池已经到上限，并且所有连接都是忙碌的
				wait(1000);
				con = getFreeConnection();
				//最好设置一个最长等待时间
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return con;
		
	}
	
	/**
	 * 等待的方法
	 * @param mseconds 等待的毫秒数
	 */
	private void wait(int mseconds) {
		try {
			Thread.sleep(mseconds);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// TimeUnit.SECONDS.sleep(1);
	}
	
	/**
	 * 获取空闲连接的方法
	 * @throws SQLException 
	 */
	private Connection getFreeConnection() throws SQLException {
		Connection con = findConnection();
		if(con == null) { // 如果获取不到可用的连接
			createConnections(incrementConnections); // 先扩容
			con = findConnection(); // 再找一下
			
			if(con == null) { // 还是为空，说明连接池已经到最大了
				//System.out.println("连接池已经到最大了");
				return null;
			}
			
		}
		return con;
	}

	/**
	 * 查找空闲链接的方法
	 * @return
	 */
	private Connection findConnection() {
		Connection con = null;
		PoolConnection pool = null;
		Enumeration<PoolConnection> enumerate = connections.elements();
		while(enumerate.hasMoreElements()) {
			pool = enumerate.nextElement(); // 获取下一个连接对象
			if(!pool.isBusy()) { // 说明是空闲的
				con = pool.getConnection(); // 获取这个连接对象中的链接
				pool.setBusy(true); // 修改这个链接对象的状态
				
				if(!testConnection(con)) { // 测试这个连接是否可用
					//创建一个新的连接来替换这个不可以用的链接
					try {
						con = newConnection();
					} catch (SQLException e) {
						e.printStackTrace();
					}
					
					pool.setConnection(con); // 替换掉这个连接对象中的连接
				}
				System.out.println(pool.getPid() + "....号链接为您服务。。。。。。");
				break;
			}
		}
		return con;
	}

	/**
	 * 测试连接是否可用
	 * @param con
	 * @return
	 */
	private boolean testConnection(Connection con) {
		try {
			con.setAutoCommit(true);	// 这是数据库事务提交方式为自动提交
		} catch (SQLException e) {
			closeConnection(con); // 释放连接
			e.printStackTrace();
			return false;
		} 
		return true;
	}

	/**
	 * 释放连接
	 * @param con
	 */
	private void closeConnection(Connection con) {
		if(con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 释放连接的方法
	 * @param con
	 */
	public void freeConnection(Connection con) {
		if(con == null) {
			return;
		}
		
		PoolConnection pool = null;
		Enumeration<PoolConnection> enumerate = connections.elements();
		while(enumerate.hasMoreElements()) {
			pool = enumerate.nextElement(); 
			if(con == pool.getConnection()) {
				pool.setBusy(false);
				return;
			}
		}
	}
	
	/**
	 * 关闭连接池的方法
	 */
	public synchronized void closeConnectionPool() {
		if(connections == null) {
			return;
		}
		
		PoolConnection pool = null;
		Enumeration<PoolConnection> enumerate = connections.elements();
		while(enumerate.hasMoreElements()) {
			pool = enumerate.nextElement(); 
			if(pool.isBusy()) { // 说明这个链接还在用
				wait(5000); // 等5秒
			}
			closeConnection(pool.getConnection()); // 关闭连接
			connections.removeElement(pool); // 从连接池中释放对象
		}
		connections = null;
	}
	
	class PoolConnection{
		private Connection connection = null;	//连接对象
		private boolean busy = false;	//是否空闲
		private int pid;	//连接编号
		
		@Override
		public String toString() {
			return "PoolConnection [busy=" + busy + ", pid=" + pid + "]";
		}

		public PoolConnection(Connection connection, int pid) {
			super();
			this.connection = connection;
			this.pid = pid;
		}

		public PoolConnection() {
			super();
		}

		public Connection getConnection() {
			return connection;
		}

		public void setConnection(Connection connection) {
			this.connection = connection;
		}

		public boolean isBusy() {
			return busy;
		}

		public void setBusy(boolean busy) {
			this.busy = busy;
		}

		public int getPid() {
			return pid;
		}

		public void setPid(int pid) {
			this.pid = pid;
		}
		
		
	}
}
